package com.maistars.spider.service.process.html.jsoup;

import com.alibaba.fastjson.JSONObject;
import com.maistars.spider.common.util.DateUtil;
import com.maistars.spider.common.util.HtmlUtil;
import com.maistars.spider.service.domain.rule.FieldRule;
import com.maistars.spider.service.process.html.HtmlProcessor;
import com.maistars.spider.service.process.html.HtmlProcessorConfig;
import org.apache.commons.lang3.StringUtils;
import org.jsoup.nodes.Element;
import org.jsoup.select.Elements;

import java.util.*;

/**
 * 提取当前节点的数据
 *
 * @author dhr
 * @date 2021/1/23 下午11:11
 */
public class ExtractProcessor extends HtmlProcessor {

    /**
     * 获取当前节点及子节点下的文本，.text()
     */
    private boolean useText;

    /**
     * 获取HTML(带格式的文本和图片等)
     */
    private boolean useHtml;

    /**
     * 比获取HTML多了本节点的HTML
     */
    private boolean useOuterHtml;

    /**
     * 仅包含本节点文本，不含子节点
     */
    private boolean useOwnText;

    /**
     * 直接抽取某个节点属性
     */
    private boolean useAttr;

    /**
     * 使用随机数或常量
     */
    private boolean useRandom;

    private JSONObject extractObject;

    private JSONObject extendObject;

    public ExtractProcessor(String htmlContent, FieldRule fieldRule, HtmlProcessorConfig htmlProcessorConfig, Map<String, Object> processorContext) {
        super(htmlContent, fieldRule, htmlProcessorConfig, processorContext);
    }

    @Override
    public HtmlProcessor init() {
        extractObject = formatOrignalProcessConfig(fieldRule.getProcessConfig());
        useText = extractObject.getBoolean("useText");
        useHtml = extractObject.getBoolean("useHtml");
        useOuterHtml = extractObject.getBoolean("useOuterHtml");
        useOwnText = extractObject.getBoolean("useOwnText");
        useAttr = extractObject.getBoolean("useAttr");
        useRandom = extractObject.getBoolean("useRandom");
        extendObject = extractObject.getJSONObject("extendConfig");
        return this;
    }

    @Override
    public List<String> process(List<String> contents) {
        List<Element> processedElements = (List<Element>) processorContext.get("processedElements");
        processedElements.stream().forEach(element -> {
            if (useText) {
                if (element == null) {
                    contents.add("");
                    return;
                }
                contents.add(element.wholeText());
            } else if (useHtml) {
                if (element == null) {
                    contents.add("");
                    return;
                }
                handleGatherImage(contents, element);
//                handleGatherLink(contents, element);
            } else if (useOuterHtml) {
                if (element == null) {
                    contents.add("");
                    return;
                }
                contents.add(element.outerHtml());
            } else if (useOwnText) {
                if (element == null) {
                    contents.add("");
                    return;
                }
                contents.add(element.ownText());
            } else if (useAttr) {
                if (element == null) {
                    contents.add("");
                    return;
                }
                // 处理属性
                contents.add(handleAttr(element));
            } else if (useRandom) {
                if (element == null) {
                    contents.add("");
                    return;
                }
                int randomType = extendObject.getInteger("randomType");
                if (randomType == 0) {
                    contents.add(extendObject.getString("defaultValue"));
                } else if (randomType == 1) {
                    Random random = new Random();
                    int maxRandomValue = extendObject.getInteger("maxRandomValue");
                    int minRandomValue = extendObject.getInteger("minRandomValue");
                    int randNumber = random.nextInt(maxRandomValue - minRandomValue + 1) + minRandomValue;
                    contents.add(String.valueOf(randNumber));
                } else if (randomType == 2) {
                    String maxRandomDate = extendObject.getString("maxRandomDate");
                    String minRandomDate = extendObject.getString("minRandomDate");
                    Date date = DateUtil.randomDate(minRandomDate, maxRandomDate, DateUtil.simpleDateFormat);
                    contents.add(DateUtil.formatDate(date, true));
                } else if (randomType == 3) {
                    String randomValue = extendObject.getString("randomStrValue");
                    String[] randomValues = randomValue.split(",");
                    if (randomValues.length > 0) {
                        Random random = new Random();
                        contents.add(String.valueOf(random.nextInt(randomValues.length)));
                    }
                }
            }
        });
        // 去除空的内容
        List<String> newContents = new ArrayList<>();
        for (String content : contents) {
            if (StringUtils.isBlank(content)) {
                continue;
            }
            newContents.add(content);
        }
        return newContents;
    }

    private void handleGatherLink(List<String> contents, Element element) {
        Boolean gatherLink = extendObject.getBoolean("gatherLink");
        if (gatherLink != null && gatherLink) {
            // 多值时的连接
            String concatDelimiter = extendObject.getString("concatDelimiter");
            if (StringUtils.isBlank(concatDelimiter)) {
                concatDelimiter = ",";
            }
            // 判断是不是a
            String urls = "";
            if (!element.is("a")) {
                contents.add("");
            }
            Elements elements = element.select("a[href]");
            if (!elements.isEmpty()) {
                for (int i = 0; i < elements.size(); i++) {
                    Element element1 = elements.get(i);
                    String href = element1.attr("href");
                    urls.concat(href);
                    if (i != (elements.size() - 1)) {
                        urls.concat(concatDelimiter);
                    }
                }
            }
            contents.add(urls);
        } else {
            contents.add(element.children().html());
        }
    }

    private void handleGatherImage(List<String> contents, Element element) {
        Boolean gatherImg = extendObject.getBoolean("gatherImg");
        if (gatherImg != null && gatherImg) {
            // 多值时的连接
            String concatDelimiter = extendObject.getString("concatDelimiter");
            if (StringUtils.isBlank(concatDelimiter)) {
                concatDelimiter = ",";
            }
            // 判断是不是只要采集图片链接
            String nodeTag = extendObject.getString("nodeTag");
            String attrName = extendObject.getString("attrName");
            List<String> attrs = new ArrayList<>();
            if (StringUtils.isNotBlank(attrName)) {
                attrs.add(attrName);
            } else {
                attrs.add("src");
                attrs.add("data-original");
                attrs.add("#src");
            }
            String imageUrls = "";
            for (String attr : attrs) {
                if (nodeTag.equals(element.tagName())) {
                    imageUrls = imageUrls.concat(element.attr(attr)).concat(",");
                } else {
                    Elements elements = element.select(nodeTag + "[" + attr + "~=(?i)\\\\.(png|jpe?g|gif)]");
                    for (Element imageElement : elements) {
                        imageUrls = imageUrls.concat(imageElement.attr(attr)).concat(",");
                    }
                }
            }
            contents.add(imageUrls);
        } else {
            contents.add(element.children().html());
        }
    }

    private String handleAttr(Element element) {
        String result = "";
        String nodeTag = extendObject.getString("nodeTag");
        String attrName = StringUtils.isBlank(extendObject.getString("attrName")) ? "href" : extendObject.getString("attrName");
        // 如果是链接节点，并且是href属性
        if ("a".equals(nodeTag)) {
            if ("href".equals(attrName.toLowerCase())) {
                if ("a".equals(element.nodeName().toLowerCase())) {
                    result = element.attr("href");
                    if (result.startsWith("javascript")) {
                        return "";
                    }
                    if (result.startsWith("//")) {
                        result = StringUtils.replace(result, "//", HtmlUtil.getUrlProtocol(htmlProcessorConfig.getProcessorConfig().getString("currentUrl")) + "://");
                    }
                } else {
                    Elements elements = element.select("a");
                    if (elements != null) {
                        // 获取拼接字符串，没有的话，默认是","
                        final StringBuilder join = new StringBuilder();
                        elements.stream().forEach(tmpElement -> {
                            String concatDelimiter = extendObject.getString("concatDelimiter");
                            if (StringUtils.isBlank(concatDelimiter)) {
                                concatDelimiter = ",";
                            }
                            String linkUrl = getLinkUrl(tmpElement, attrName);
                            if (!StringUtils.isBlank(linkUrl)) {
                                join.append(linkUrl);
                                join.append(concatDelimiter);
                            }
                        });
                        result = join.toString();
                    }
                }
            }
        } else if ("img".equals(nodeTag)) {
            if ("img".toLowerCase().equals(element.nodeName())) {
                result = getImgUrl(element, attrName);
            } else {
                Elements elements = element.select("img");
                if (elements != null) {
                    // 获取拼接字符串，没有的话，默认是";"
                    final StringBuilder join = new StringBuilder();
                    elements.stream().forEach(tmpElement -> {
                        String concatDelimiter = extendObject.getString("concatDelimiter");
                        if (StringUtils.isBlank(concatDelimiter)) {
                            concatDelimiter = "\t";
                        }
                        String imageUrl = getImgUrl(tmpElement, attrName);
                        if (!StringUtils.isBlank(imageUrl)) {
                            join.append(imageUrl);
                            join.append(concatDelimiter);
                        }
                    });
                    result = join.toString();
                }
            }
        } else {
            result = element.attr(attrName);
        }
        return result;
    }

    private String getLinkUrl(Element element, String attrName) {
        if (!StringUtils.isBlank(attrName)) {
            return element.attr(attrName);
        }

        String[] attrNames = new String[]{"href"};
        for (String name : attrNames) {
            String result = element.attr(name);
            if (result != null) {
                if (result.startsWith("//")) {
                    result = StringUtils.replace(result, "//", HtmlUtil.getUrlProtocol(htmlProcessorConfig.getProcessorConfig().getString("currentUrl")) + "://");
                }
                return result;
            }
        }
        return "";
    }

    private String getImgUrl(Element element, String attrName) {
        if (!StringUtils.isBlank(attrName)) {
            return element.attr(attrName);
        }

        String[] attrNames = new String[]{"data-src", "data-url", "data-original", "#src", "src"};
        for (String name : attrNames) {
            String result = element.attr(name);
            if (!StringUtils.isBlank(result)) {
                return result;
            }
        }
        return "";
    }

    private JSONObject formatOrignalProcessConfig(String orignalProcessConfig) {
        JSONObject orinalProcessConfig = JSONObject.parseObject(orignalProcessConfig);
        JSONObject extractConfig = new JSONObject();
        boolean useText = orinalProcessConfig.getBoolean("useText") == null ? false : orinalProcessConfig.getBoolean("useText");
        if (!useText) {
            useText = "text".equals(orinalProcessConfig.getString("valueType"));
        }
        extractConfig.put("useText", useText);
        extractConfig.put("useHtml", orinalProcessConfig.getBoolean("useHtml") == null ? false : orinalProcessConfig.getBoolean("useHtml"));
        extractConfig.put("useOuterHtml", orinalProcessConfig.getBoolean("useOuterHtml") == null ? false : orinalProcessConfig.getBoolean("useOuterHtml"));
        extractConfig.put("useOwnText", orinalProcessConfig.getBoolean("useOwnText") == null ? false : orinalProcessConfig.getBoolean("useOwnText"));
        extractConfig.put("useRandom", orinalProcessConfig.getBoolean("useRandom") == null ? false : orinalProcessConfig.getBoolean("useRandom"));
        extractConfig.put("useAttr", orinalProcessConfig.getBoolean("useAttr") == null ? false : orinalProcessConfig.getBoolean("useAttr"));

        extractConfig.put("extendConfig",new JSONObject());

        if (orinalProcessConfig.getBoolean("useLink") == null ? false : orinalProcessConfig.getBoolean("useLink")) {
            extractConfig.put("useAttr", true);
            extractConfig.getJSONObject("extendConfig").put("nodeTag", "a");
            extractConfig.getJSONObject("extendConfig").put("attrName", extractConfig.getString("attrName"));
        } else if (orinalProcessConfig.getBoolean("useImg") == null ? false : orinalProcessConfig.getBoolean("useImg")) {
            extractConfig.put("useHtml", true);
            extractConfig.getJSONObject("extendConfig").put("gatherImg", "true");
            extractConfig.getJSONObject("extendConfig").put("nodeTag", StringUtils.isBlank(orinalProcessConfig.getString("imgAttr")) ? "img" : orinalProcessConfig.getString("imgAttr"));
            extractConfig.getJSONObject("extendConfig").put("attrName", extractConfig.getString("attrName"));
        }
        return extractConfig;
    }
}
